package com.hechuan.interceptor;

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.UnknownTypeHandler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 查询拦截器4
 */
public class QueryInterceptor4 implements InnerInterceptor {

    /**
     * 复杂一点---详细说明
     * 优点：预编译sql条件
     */
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 原始sql：SELECT  id,name,age,address  FROM t_user WHERE (name = ? AND age = ?)
        String originSql = boundSql.getSql();
        System.out.println("originSql：" + originSql);

        // 修改后的sql
        String targetSql = "SELECT id,name,age,address FROM t_user WHERE age = ? and (address = ? or address = ?)";

        // 清空 绑定值映射路径（这里只是清空 绑定值的 映射路径，真正的值存在 parameter对象中，ew键对应的 QueryWrapper对象中的paramNameValuePairs中）
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        parameterMappings.clear();

        // 添加绑定值 映射路径
        // 这两个参数 configuration、unknownTypeHandler，只是创建出来，并没有发现有什么用，只是让ParameterMapping.Builder()，能正常通过。
        Configuration configuration = new Configuration();
        UnknownTypeHandler unknownTypeHandler = new UnknownTypeHandler(configuration);

        // 如果觉得不妥，还有一种方式，如若 parameterMappings集合中原来就有值，则取出其中一个来获取这两个对象，因为这两个对象configuration、unknownTypeHandler 在多个parameterMapping对象中都是用的一个。
        // Configuration configuration = (Configuration) ReflectUtil.getFieldValue(parameterMappings.get(0), "configuration");
        // UnknownTypeHandler unknownTypeHandler = (UnknownTypeHandler) ReflectUtil.getFieldValue(parameterMappings.get(0), "typeHandler");

        // ew.paramNameValuePairs.MPGENVAL1，固定写法，第一个映射路径就写：MPGENVAL1
        ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, "ew.paramNameValuePairs.MPGENVAL1", unknownTypeHandler);
        ParameterMapping parameterMapping = builder.build();
        // 添加绑定值 映射路径
        parameterMappings.add(parameterMapping);

        // 若有多个参数 则构建多个：绑定值映射路径。MPGENVAL1后面的数值需要加1
        // （类似JDBC中的 PreparedStatement，设置值，按顺序设置。）
         ParameterMapping.Builder builder2 = new ParameterMapping.Builder(configuration, "ew.paramNameValuePairs.MPGENVAL2", unknownTypeHandler);
         ParameterMapping parameterMapping2 = builder2.build();
        // 添加绑定值 映射路径
         parameterMappings.add(parameterMapping2);

         ParameterMapping.Builder builder3 = new ParameterMapping.Builder(configuration, "ew.paramNameValuePairs.MPGENVAL3", unknownTypeHandler);
         ParameterMapping parameterMapping3 = builder3.build();
        // 添加绑定值 映射路径
         parameterMappings.add(parameterMapping3);

        // 添加绑定值
        Map map = (Map) parameter;
        // 获取 QueryWrapper 对象
        QueryWrapper queryWrapper = (QueryWrapper) map.get("ew");

        // 通过反射获取 queryWrapper中 paramNameValuePairs的值
        // 这里使用一个工具(hutool)设置，如若直接使用反射获取QueryWrapper对象中的值会报错，找不到对应的字段
        // 因为paramNameValuePairs字段是QueryWrapper对象父级对象中的字段
        // 工具会循环向上查找
        HashMap hashMap = (HashMap)ReflectUtil.getFieldValue(queryWrapper, "paramNameValuePairs");
        // 将原有的绑定值清空
        hashMap.clear();

        // 添加新值，这里设置的key 要与 绑定值映射路径最后一级保持一致
        hashMap.put("MPGENVAL1", 18);// 第1个 “?”

        // 若需要多个参数 则添加多个值（类似JDBC中的 PreparedStatement，设置值，按顺序设置。）
        // 这里的key，要对应 绑定值映射路径的最后一级，例如：MPGENVAL2 对应 ew.paramNameValuePairs.MPGENVAL2
         hashMap.put("MPGENVAL2", "上海");// 第2个 “?”
         hashMap.put("MPGENVAL3", "北京");// 第3个 “?”

        // 最终执行的sql，例如：SELECT id,name,age,address FROM t_user WHERE age = 18 and (address = '上海' or address = '北京')

        // 设置 绑定参数数量，有几个参数写几。（实际上不加这一行也可以）
        // ReflectUtil.setFieldValue(queryWrapper, "paramNameSeq", new AtomicInteger(2));

        // 修改完成的sql 再设置回去
        PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
        mpBoundSql.sql(targetSql);
    }

}
